import sqlite3,re
from urllib.request import Request, urlopen


class DataTool(object):
    """
    工具类: 对提取的元组中的数据，进行整理，删除无效字符(\n,<br/>)
    """
    # 定义删除\n字符的正则表达式
    pattern_n = re.compile(r'\n', re.S)
    # 定义删除<br/>字符的正则表达式
    pattern_br = re.compile(r'br/', re.S)

    pattern_space = re.compile(r' ', re.S)

    def process_tuple_data(self, origin_tuple_data):
        """
        用于对提取的原始元祖进行数据处理的函数
        :param origin_tuple_data:  原始数据元组
        :return:  返回整理之后的元组
        """
        # 需要处理的数据: 用户昵称、段子内容.
        # sub()是正则表达式中的替换数据的方法，需要将\n字符替换成空字符
        # 参数： 1.替换规则 2.替换结果 3.要匹配的字符串
        nick_name = re.sub(self.pattern_n, '', origin_tuple_data[2])

        #处理段子内容
        content = re.sub(self.pattern_n, '', origin_tuple_data[3])
        content = re.sub(self.pattern_br, '', content)

        result = re.sub(self.pattern_space, '', origin_tuple_data[2])
        content = re.sub(self.pattern_space, '', content)
        # 将处理后的数据再封装成一个元祖，返回
        data = (origin_tuple_data[0], origin_tuple_data[1], result, content, origin_tuple_data[4], origin_tuple_data[5], origin_tuple_data[6], origin_tuple_data[7])
        return data


class DBTool(object):
    """
    将数据保存在数据库中的工作类，主要负责数据库的增删改查操作。
    """
    connct = None
    cursor = None
    # 操作数据库：1.创建数据库的连接对象，创建游标，这两个对象一般连接一次即可; 2.数据的增删改查； 3.关闭数据库对象，关闭游标对象。一般都是在数据保存完毕之后关闭即可。
    @classmethod
    def create_db_cursor(cls):
        cls.connect = sqlite3.connect('qsbk.db')
        cls.cursor = cls.connect.cursor()

    @classmethod
    def insert_sql(cls, tuple_data):
        """
        执行数据插入语句的函数
        :param tuple_data:  使用DataTool工具类整理过后的数据
        :return:
        """

    @classmethod
    def close_db_cursor(cls):
        cls.cursor.close()
        cls.connect.close()


class QSBKSpider(object):
    """
    爬虫类
    """
    def __init__(self):
        # 将各个页面通用的路径， 不变的路径声明称为属性，调用方便，直接在这个属性的后面拼接页码。
        self.base_url = 'https://www.renrenche.com/zz/ershouche/ '
        # 初始化请求头， 伪造浏览器请求头中的User-Agent字段值，有个默认的值,User-Agent ：Python- 3.7 xxx。
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
        }
        # 实例化工具类DataTool的对象
        self.tool = DataTool()

    def get_list_html(self, page_num):
        """
        会被重复调用
        获取每一个列表页的html网页源代码(这个获取的源代码就是"右键-网页源代码"中的内容)
        page_num: 表示将要请求的页面的页码。
        :return:
        """

        # 构造每一页url地址
        page_url =self.base_url
        # 向page_url发送GET请求，开始获取当前页page_num的网页源代码
        # 先构造一个Request请求对象
        request = Request(page_url, headers=self.headers)
        try:
            response = urlopen(request)
        except Exception as e:
            print("请求失败: 地址{} ,原因".format(page_url, e))
            return None
        else:
            # try语句中的请求没有出现异常， 就会执行else语句，如果出现异常了就不会执行else语句了
            # print(response)
            return response.read().decode()

    def parse_list_html(self, html):
        """
        解析上一个函数请求的html源代码
        :param html:  请求成功返回列表页的网页源代码，请求失败返回None
        :return:
        """
        if html:
            # 使用正则表达式开始解析网页源代码
            # 写正则注意事项
            # 1. 尽量找到要匹配的零散数据所在的标签，而且这个标签必须和这些零散的数据一样能够循环。因为findall()函数在循环匹配数据的时候，是按照整个正则表达式规则循环匹配的。
            # 2.在参考网页中"审查元素"来设置正则匹配规则的时候，一定要确认是否和 "网页源代码"中的标签数序、属性顺序等保持一致，如果不一致的话，必须参考"网页源代码"来设置正则匹配原则。因为"审查元素"中真的html代码是经过js渲染之后的源代码。
            # pattern = re.compile(r'<li class="span6 list-item car-item.*?>.*?<h3 .*?>.*?<img .*?>(.*?)</h3>.*?<span .*?>(.*?)<em .*?>.*?</em>(.*?)</span>.*?<div class="mileage-tag-box">.*?</div>.*?<div class="price">(.*?)<span>(.*?)</span>.*?<div .*?>(.*?)<div .*?>(.*?)</div>(.*?)</div>', re.S)
            pattern = re.compile(r'<li class="span6 list-item car-item .*?" .*?>.*?<a rrc-event-name="position.*?>.*?<h3.*?>.*?<img src="http.*?">(.*?)</h3>.*?<span .*?>(.*?)<em .*?>.*?</em>(.*?)</span>.*?<div class="mileage-tag-box">.*?</div>.*?<div class="price">(.*?)<span>(.*?)</span>.*?<div .*?>(.*?)<div .*?>(.*?)</div>(.*?)</div>', re.S)

            results_list = re.findall(pattern, html)
            for data in results_list:
                new_data = self.tool.process_tuple_data(data)
                print(new_data)
        else:
            print("html源代码为None")

# 创建数据库对象、游标对象
DBTool.create_db_cursor()

obj = QSBKSpider()
html = obj.get_list_html(None)
obj.parse_list_html(html)

# 关闭数据库、 游标对象
